﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Office.Interop.Outlook;
using System.IO;
using System.Security;
using System.Collections;

namespace ContactsTester
{
    public sealed class SyncManager
    {
        private readonly string _userName;
        private readonly string _password;
        private readonly string _userMode;
        public static event Sync.Events.SyncEvents.ContactExists OnContactExists;
        public event Sync.Events.SyncEvents.SyncContacts OnSyncContacts;
        public event Sync.Events.SyncEvents.SaveContacts OnSaveContacts;

        /// <summary>
        /// This is the initial contructor for the class , it contains all the required info to sucessfully get
        /// the users contact details.
        /// </summary>
        /// <param name="UserName">The Users google username</param>
        /// <param name="Password">The Users google password</param>
        /// <param name="UserMode">The mode the the user wishes to execute</param>
        public SyncManager(string GoogleUserName, string GooglePassword, string UserMode)
        {
            OnSaveContacts += new Sync.Events.SyncEvents.SaveContacts(Sync.Events.EventHandlers.SyncManager_OnSaveContacts);
            OnSyncContacts += new Sync.Events.SyncEvents.SyncContacts(Sync.Events.EventHandlers.SyncManager_OnSyncContacts);
            OnContactExists += new Sync.Events.SyncEvents.ContactExists(Sync.Events.EventHandlers.SyncManager_OnContactExists);
            _userName = GoogleUserName;
            _password = GooglePassword;
            _userMode = UserMode;
        }



        /// <summary>
        /// Saves all the users google contacts to vCards
        /// </summary>
        public void SaveGoogleContactsTovFile()
        {
            GoogleContactConntector currentGoogleConnection = new GoogleContactConntector(_userName, _password);

            SyncContact[] googleContacts = currentGoogleConnection.LoadGoogleContactDetails();
            if (!Directory.Exists("C:\\vCards"))//Allow user to select the filelocation to save to at some stage.
            {
                Directory.CreateDirectory("C:\\vCards");//Allow user to select the filelocation to save to at some stage.
            }
            FileStream fs = new FileStream("C:\\vCards\\googleContacts.vcf", FileMode.OpenOrCreate);//Allow user to select the filelocation to save to at some stage.

            foreach (SyncContact currentContact in googleContacts)
            {
                LumiSoft.Net.Mime.vCard.vCard newCard = new LumiSoft.Net.Mime.vCard.vCard();
                newCard.EmailAddresses.Add(LumiSoft.Net.Mime.vCard.EmailAddressType_enum.Internet, currentContact.EmailAddress);
                newCard.FormattedName = currentContact.FullName;
                newCard.PhoneNumbers.Add(LumiSoft.Net.Mime.vCard.PhoneNumberType_enum.Home, currentContact.HomeNumber);
                newCard.PhoneNumbers.Add(LumiSoft.Net.Mime.vCard.PhoneNumberType_enum.Work, currentContact.WorkNumber);
                newCard.ToFile(fs);
                fs.Flush();
                Console.Write("*");//HACK : Remove Symbols When done testing.
            }
            fs.Close();
        }

        /// <summary>
        /// Saves all the users google contacts to vCards
        /// </summary>
        public void SaveGoogleContactsToMobileFile()
        {
            GoogleContactConntector currentGoogleConnection = new GoogleContactConntector(_userName, _password);

            SyncContact[] googleContacts = currentGoogleConnection.LoadGoogleContactDetails();
            StringBuilder currentUserStream = new StringBuilder();
            foreach (SyncContact currentContact in googleContacts)
            {
                LumiSoft.Net.Mime.vCard.vCard newCard = new LumiSoft.Net.Mime.vCard.vCard();
                newCard.EmailAddresses.Add(LumiSoft.Net.Mime.vCard.EmailAddressType_enum.Internet, currentContact.EmailAddress);
                newCard.FormattedName = currentContact.FullName;
                newCard.PhoneNumbers.Add(LumiSoft.Net.Mime.vCard.PhoneNumberType_enum.Home, currentContact.HomeNumber);
                newCard.PhoneNumbers.Add(LumiSoft.Net.Mime.vCard.PhoneNumberType_enum.Work, currentContact.WorkNumber);
                currentUserStream = newCard.ToString(currentUserStream);
            }

            MyTestClass.GoogleMobileFileGeneratorPortTypeClient mobiGen = new ContactsTester.MyTestClass.GoogleMobileFileGeneratorPortTypeClient();
            string returnFileName = mobiGen.SyncFileGenerator(new ContactsTester.MyTestClass.SyncFileGeneratorRequest(currentUserStream.ToString())).@return;
            Console.WriteLine(returnFileName);
        }
        public SyncContact[] GetFileContacts(string fileName)
        {
            ArrayList vCardContacts = new ArrayList();
            StreamReader sr = new StreamReader(fileName);
            string line = "";
            bool inVcard = false;
            SyncContact currContact = new SyncContact();
            while (!sr.EndOfStream)
            {
                line = sr.ReadLine();

                switch (line)
                {
                    case "BEGIN:VCARD":
                        {
                            inVcard = true;
                            break;
                        }
                    case "END:VCARD":
                        {
                            //save
                            vCardContacts.Add(currContact);
                            //create new
                            currContact = new SyncContact();
                            inVcard = false;
                            break;
                        }
                    default:
                        break;
                }
                if (line.StartsWith("FN"))
                {
                    string[] fn = line.Split(':');
                    currContact.FullName = fn[1];
                }
                string[] lineArr = line.Split(';');
                string[] attrIter = new string[10];
                if (lineArr.Length > 1)
                {
                    switch (lineArr[0])
                    {
                        case "EMAIL":
                            {
                                attrIter = lineArr[1].Split(':');
                                if (attrIter[0] == "INTERNET")
                                {
                                    currContact.EmailAddress = attrIter[1];
                                }
                                break;
                            }
                        case "TEL":
                            {
                                attrIter = lineArr[1].Split(':');
                                if (attrIter[0] == "HOME")
                                {
                                    currContact.HomeNumber = attrIter[1];
                                }
                                else if (attrIter[0] == "WORK")
                                {
                                    currContact.WorkNumber = attrIter[1];
                                }
                                break;
                            }
                        default:
                            break;
                    }
                }
            }
            //return array of contacts
            return (SyncContact[])vCardContacts.ToArray(System.Type.GetType("ContactsTester.SyncContact"));
        }

        /// <summary>
        /// Saves all the users Outlook contacts to vCards
        /// </summary>
        public void SaveOutlookContactsTovFile()
        {
            ApplicationClass currentAppInstance = new ApplicationClass();

            SyncContact[] outlookContacts = OutlookConnector.LoadOutlookContactDetails(currentAppInstance);

            if (!Directory.Exists("C:\\vCards"))//Allow user to select the filelocation to save to at some stage.
            {
                Directory.CreateDirectory("C:\\vCards");//Allow user to select the filelocation to save to at some stage.
            }
            FileStream fs = new FileStream("C:\\vCards\\googleContacts.vcf", FileMode.OpenOrCreate);//Allow user to select the filelocation to save to at some stage.

            foreach (SyncContact currentContact in outlookContacts)
            {
                LumiSoft.Net.Mime.vCard.vCard newCard = new LumiSoft.Net.Mime.vCard.vCard();
                newCard.EmailAddresses.Add(LumiSoft.Net.Mime.vCard.EmailAddressType_enum.Internet, currentContact.EmailAddress);
                newCard.FormattedName = currentContact.FullName;
                newCard.PhoneNumbers.Add(LumiSoft.Net.Mime.vCard.PhoneNumberType_enum.Home, currentContact.HomeNumber);
                newCard.PhoneNumbers.Add(LumiSoft.Net.Mime.vCard.PhoneNumberType_enum.Work, currentContact.WorkNumber);
                newCard.ToFile(fs);
                fs.Flush();
                Console.Write("*");//HACK : Remove Symbols When done testing.
            }
            fs.Close();
        }

        /// <summary>
        /// Saves all the users contacts to vCardFiles.
        /// This includes Outlook and Google
        /// </summary>
        public void SaveAllContactsTovCardFile()
        {
            OnSaveContacts(new Sync.Events.SaveEventArgs(SaveType.vCard));
            SaveGoogleContactsTovFile();
            SaveOutlookContactsTovFile();
        }

        public void SaveVcardContactsToGoogle(string fileName)
        {
            SyncContact[] fileContacts = GetFileContacts(fileName);
            ApplicationClass currentAppInstance = new ApplicationClass();
            GoogleContactConntector currentGoogleConnection = new GoogleContactConntector(_userName, _password);
            SyncContact[] googleContacts = currentGoogleConnection.LoadGoogleContactDetails();
            UpdateGoogleContacts(currentAppInstance, currentGoogleConnection, googleContacts, fileContacts);
        }

        /// <summary>
        /// Sync the users contacts , and update where needed.
        /// </summary>
        public void SyncContacts()
        {
            OnSyncContacts(new EventArgs());
            ApplicationClass currentAppInstance = new ApplicationClass();

            GoogleContactConntector currentGoogleConnection = new GoogleContactConntector(_userName, _password);

            SyncContact[] googleContacts = currentGoogleConnection.LoadGoogleContactDetails();

            SyncContact[] outlookContacts = OutlookConnector.LoadOutlookContactDetails(currentAppInstance);

            UpdateGoogleContacts(currentAppInstance, currentGoogleConnection, googleContacts, outlookContacts);
            foreach (SyncContact oSyncContact in outlookContacts)
            {
                //Console.WriteLine(oSyncContact.FullName);
                Console.Write("/");//HACK : Remove Symbols When done testing.
                if (!SyncContact.Exists(oSyncContact.FullName, googleContacts))
                {
                    Console.Write("\\");//HACK : Remove Symbols When done testing.
                    oSyncContact.InsertMissingContact(currentGoogleConnection.CurrentServiceConnection
                           , currentAppInstance
                           , currentGoogleConnection.ContactsFromGoogle
                           , UpdateType.Gmail);
                }
                else
                {
                    if (_userMode != null)
                    {
                        UpdateContact(currentGoogleConnection, googleContacts, oSyncContact);
                    }
                }
            }
        }

        private void UpdateGoogleContacts(ApplicationClass currentAppInstance, GoogleContactConntector currentGoogleConnection, SyncContact[] googleContacts, SyncContact[] otherContacts)
        {
            Console.WriteLine("Updating Google Contacts");
            foreach (SyncContact gSyncContact in googleContacts)
            {
                Console.Write("_");//HACK : Remove Symbols When done testing.

                if (!SyncContact.Exists(gSyncContact.FullName, otherContacts))
                {
                    Console.Write("-");//HACK : Remove Symbols When done testing.
                    gSyncContact.InsertMissingContact(currentGoogleConnection.CurrentServiceConnection
                           , currentAppInstance
                           , currentGoogleConnection.ContactsFromGoogle
                           , UpdateType.Outlook);
                }
                else
                {
                    if (_userMode != null)
                    {
                        UpdateContact(currentGoogleConnection, otherContacts, gSyncContact);
                    }
                }

            }
        }



        private void UpdateContact(GoogleContactConntector currentGoogleConnection, SyncContact[] outlookContacts, SyncContact gSyncContact)
        {
            SyncContact contactToUpdate = GetContact(gSyncContact.FullName, outlookContacts);
            switch (_userMode.ToLower())
            {
                case "g":
                    UpdateContactSpecificDetails(gSyncContact, contactToUpdate, UpdateType.Gmail);
                    gSyncContact.UpdateContactDetails(currentGoogleConnection.CurrentServiceConnection, UpdateType.Gmail);
                    break;
                case "o":
                    UpdateContactSpecificDetails(gSyncContact, contactToUpdate, UpdateType.Outlook);
                    contactToUpdate.UpdateContactDetails(currentGoogleConnection.CurrentServiceConnection, UpdateType.Outlook);
                    break;
            }
        }

        /// <summary>
        /// Gets a specific contacts from the specified search list
        /// </summary>
        /// <param name="ContactName">Contact To Find</param>
        /// <param name="contactsToSearch">List To Search</param>
        /// <returns>The contact that matches the name in the list</returns>
        private static SyncContact GetContact(string ContactName, SyncContact[] contactsToSearch)
        {
            foreach (SyncContact contact in contactsToSearch)
            {
                if (contact.FullName == null)
                {
                    return null;
                }
                if (contact.FullName.ToLower() == ContactName.ToLower())
                {
                    return contact;
                }
            }
            return null;
        }

        /// <summary>
        /// This method allows the sync manager to update the relevant sync contact with the others details;
        /// </summary>
        /// <param name="gSyncContact">The Google Contact to update</param>
        /// <param name="oSyncContact">The outlook contact to update</param>
        /// <param name="updateMode">The update mode determines which of the sync contacts is updated.</param>
        private static void UpdateContactSpecificDetails(SyncContact gSyncContact, SyncContact oSyncContact, UpdateType updateMode)
        {
            switch (updateMode)
            {
                case UpdateType.Outlook:
                    oSyncContact.Company = gSyncContact.Company;
                    oSyncContact.EmailAddress = gSyncContact.EmailAddress;
                    oSyncContact.HomeNumber = gSyncContact.HomeNumber;
                    oSyncContact.MobileNumber = gSyncContact.MobileNumber;
                    oSyncContact.Notes = gSyncContact.Notes;
                    oSyncContact.WorkNumber = gSyncContact.WorkNumber;
                    break;
                case UpdateType.Gmail:
                    gSyncContact.Company = oSyncContact.Company;
                    gSyncContact.EmailAddress = oSyncContact.EmailAddress;
                    gSyncContact.HomeNumber = oSyncContact.HomeNumber;
                    gSyncContact.MobileNumber = oSyncContact.MobileNumber;
                    gSyncContact.Notes = oSyncContact.Notes;
                    gSyncContact.WorkNumber = oSyncContact.WorkNumber;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("updateMode");
            }

        }
    }
}
